﻿using System;
using System.IO;
using System.Security.Cryptography;
namespace WCFAuthorization.Service.Crypt
{
    public class SymmetricCryptographer : IDisposable
    {
        private SymmetricAlgorithm algorithm;
        private ProtectedKey key;   //key=ProtectedKey.CreateFromPlaintextKey()

        private byte[] Key
        {
            get
            {
                return key.DecryptedKey; //返回由DPAPI解密出来 ，对称加密算法的key
            }
        }
        /// <summary>
        /// [构造函数]   创建一个对称加密实例
        /// </summary>
        /// <param name="algorithmType"></param>
        /// <param name="key"></param>
        public SymmetricCryptographer(Type algorithmType, ProtectedKey key)
        {
            if (algorithmType == null) throw new ArgumentNullException("algorithmType Null");
            if (!typeof(SymmetricAlgorithm).IsAssignableFrom(algorithmType)) throw new ArgumentException("algorithmType");
            if (key == null) throw new ArgumentNullException("key Null");
            this.key = key;
            this.algorithm = GetSymmetricAlgorithm(algorithmType);
        }
        public SymmetricCryptographer(Type algorithmType)
        {
            if (algorithmType == null) throw new ArgumentNullException("algorithmType Null");
            if (!typeof(SymmetricAlgorithm).IsAssignableFrom(algorithmType)) throw new ArgumentException("algorithmType");
            this.algorithm = GetSymmetricAlgorithm(algorithmType);
        }
        /// <summary>
        /// Finalizer for <see cref="SymmetricCryptographer"/>
        /// </summary>
        ~SymmetricCryptographer()
        {
            Dispose(false);
        }

        /// <summary>
        /// Releases all resources for this instance.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Override to customize resources to be disposed.
        /// </summary>
        /// <param name="disposing">Unused.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (algorithm != null)
            {
                algorithm.Clear();
                algorithm = null;
            }
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="plaintext"></param>
        /// <returns></returns>
        public byte[] Encrypt(byte[] plaintext)
        {
            byte[] output = null;
            byte[] cipherText = null;

            this.algorithm.Key = Key; //加密算法对象的key值

            using (ICryptoTransform transform = this.algorithm.CreateEncryptor()) //[CreateEncryptor]:用当前的 Key 属性和初始化向量 (IV) 创建对称加密器对象
            {
                cipherText = Transform(transform, plaintext);
            }

            output = new byte[IVLength + cipherText.Length];
            Buffer.BlockCopy(this.algorithm.IV, 0, output, 0, IVLength);//[BlockCopy]:将指定数目的字节从起始于特定偏移量的源数组复制到起始于特定偏移量的目标数组。
            Buffer.BlockCopy(cipherText, 0, output, IVLength, cipherText.Length);

            CryptographyUtility.ZeroOutBytes(this.algorithm.Key);

            return output;
        }
        public byte[] Encrypt(byte[] plaintext,byte[] key)
        {
            byte[] output = null;
            byte[] cipherText = null;

            this.algorithm.Key = key; 

            using (ICryptoTransform transform = this.algorithm.CreateEncryptor()) //[CreateEncryptor]:用当前的 Key 属性和初始化向量 (IV) 创建对称加密器对象
            {
                cipherText = Transform(transform, plaintext);
            }

            output = new byte[IVLength + cipherText.Length];
            Buffer.BlockCopy(this.algorithm.IV, 0, output, 0, IVLength);//[BlockCopy]:将指定数目的字节从起始于特定偏移量的源数组复制到起始于特定偏移量的目标数组。
            Buffer.BlockCopy(cipherText, 0, output, IVLength, cipherText.Length);

            CryptographyUtility.ZeroOutBytes(this.algorithm.Key);

            return output;
        }

        /// <summary>
        /// <para>Decrypts bytes with the initialized algorithm and key.</para>
        /// </summary>
        /// <param name="encryptedText"><para>The text which you wish to decrypt.</para></param>
        /// <returns><para>The resulting plaintext.</para></returns>
        public byte[] Decrypt(byte[] encryptedText)
        {
            byte[] output = null;
            byte[] data = ExtractIV(encryptedText);

            this.algorithm.Key = Key;

            using (ICryptoTransform transform = this.algorithm.CreateDecryptor())
            {
                output = Transform(transform, data);
            }

            CryptographyUtility.ZeroOutBytes(this.algorithm.Key);

            return output;
        }
        public byte[] Decrypt(byte[] encryptedText,byte[] key)
        {
            byte[] output = null;
            byte[] data = ExtractIV(encryptedText);

            this.algorithm.Key = key;

            using (ICryptoTransform transform = this.algorithm.CreateDecryptor())
            {
                output = Transform(transform, data);
            }

            CryptographyUtility.ZeroOutBytes(this.algorithm.Key);

            return output;
        }

        private static byte[] Transform(ICryptoTransform transform, byte[] buffer)
        {
            return CryptographyUtility.Transform(transform, buffer);
        }

        private int IVLength
        {
            get
            {
                if (this.algorithm.IV == null)
                {
                    this.algorithm.GenerateIV();
                }
                return this.algorithm.IV.Length;
            }
        }

        private byte[] ExtractIV(byte[] encryptedText)
        {
            byte[] initVector = new byte[IVLength];

            if (encryptedText.Length < IVLength + 1)
            {
                throw new CryptographicException("ExceptionDecrypting");
            }

            byte[] data = new byte[encryptedText.Length - IVLength];

            Buffer.BlockCopy(encryptedText, 0, initVector, 0, IVLength);
            Buffer.BlockCopy(encryptedText, IVLength, data, 0, data.Length);

            this.algorithm.IV = initVector;

            return data;
        }

        /// <summary>
        /// 根据加密类型获取一个SymmetricAlgorithm对象
        /// </summary>
        /// <param name="algorithmType"></param>
        /// <returns></returns>
        private static SymmetricAlgorithm GetSymmetricAlgorithm(Type algorithmType)
        {
            SymmetricAlgorithm symmetricAlgorithm = Activator.CreateInstance(algorithmType) as SymmetricAlgorithm;
            return symmetricAlgorithm;
        }
        //[SymmetricAlgorithm]:表示所有对称算法的实现都必须从中继承的抽象基类。  
    }
}
